// 
// SoapTrace.Net - A soap message monitoring utility. 
// 
// Copyright (C) 2004 Matthew Ward
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
// 
// Matthew Ward (mrward@users.sourceforge.net)
// 

using System;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace SoapTrace.Tests.Utils
{
	/// <summary>
	/// Client of the <see cref="MockRemoteServer"/>.  Encapsulates
	/// the message format used to communicate with the MockRemoteServer.
	/// </summary>
	/// <remarks>
	/// This client communicates with the remote server using Tcp.
	/// </remarks>
	public class MockRemoteServerClient
	{
		/// <summary>
		/// The underling tcp client.
		/// </summary>
		private TcpClient m_TcpClient;

		/// <summary>
		/// The read buffer.
		/// </summary>
		private byte[ ] m_ReadBytes = new byte[ 4096 ];

		/// <summary>
		/// Flag indicating whether the client is connected to the server.
		/// </summary>
		private bool m_Connected;

		/// <summary>
		/// The message that is received from the remote server.
		/// </summary>
		private MockRemoteServerMessage m_Message = new MockRemoteServerMessage( );
		
		/// <summary>
		/// Creates a new instance of this class.
		/// </summary>
		public MockRemoteServerClient( )
		{
		}
		
		/// <summary>
		/// Gets whether the client is connected to a server.
		/// </summary>
		public bool IsConnected
		{
			[ DebuggerStepThrough ]
			get
			{
				return m_Connected;
			}
		}

		/// <summary>
		/// Connects the client to the server.
		/// </summary>
		/// <param name="address">The server's ip address.</param>
		/// <param name="port">The server's port.</param>
		public void Connect( string address, int port )
		{
			m_TcpClient = new TcpClient( );
			m_TcpClient.Connect( address, port );

			m_Connected = true;
		}
		
		/// <summary>
		/// Closes the connection with the server.
		/// </summary>
		public void Close( )
		{
			if( m_TcpClient != null )
			{
				m_TcpClient.Close( );
				m_TcpClient = null;

				m_Connected = false;
			}
		}
		
		/// <summary>
		/// Sends a message to the server.
		/// </summary>
		/// <param name="message">The message to send.</param>
		public void Send( string message )
		{
			// Send message.
			byte[ ] MessageBytes = MockRemoteServerMessage.GetBytes( message );
			NetworkStream Stream = m_TcpClient.GetStream( );
			Stream.Write( MessageBytes, 0, MessageBytes.Length );
		}
		
		/// <summary>
		/// Reads a message sent from the server.
		/// </summary>
		/// <param name="message">The message read.</param>
		/// <param name="timeout">The read timeout in milliseconds.</param>
		/// <returns>Status flag indicating whether the read was successful (true) 
		/// or the connection was closed by the server (false).</returns>
		public bool Read( out string message, int timeout )
		{
			bool ReceivedMessage = false;
			message = String.Empty;

			// Clear any stored message.

			m_Message.Clear( );
		
			// Set read timeout.

			DateTime EndTime = DateTime.Now + new TimeSpan( 0, 0, 0, 0, timeout );
			int ReadTimeout = timeout;

	
			// Read the data.

			Debug.WriteLine( "Waiting for client message..." );
				
			bool Continue = true;
			
			while( Continue )
			{
				ReceivedMessage = ReadMessage( ReadTimeout );
				
				if( ReceivedMessage )
				{
					Continue = false;

					message = m_Message.GetString( );
				}
				else if( IsConnected )
				{
					// Keep reading data.

					TimeSpan TimeLeft = EndTime - DateTime.Now;
					ReadTimeout -= TimeLeft.Milliseconds;
				}
				else
				{
					// Connection was closed by server.

					Continue = false;
				}
			}

			return ReceivedMessage;
		}

		/// <summary>
		/// Reads data bytes from the tcp client connection.  If the client is
		/// disconnected from the server then the IsConnected property will
		/// be set by this method.  
		/// </summary>
		/// <remarks>This method only makes only one call
		/// to TcpClient.Read so it is likely that the complete message
		/// will not be received in one go.  If a complete message is read in
		/// one go then this method will return true.</remarks>
		/// <returns>A status flag indicating whether a complete message
		/// was read from the server.</returns>
		private bool ReadMessage( int timeout )
		{
			bool MessageReceived = false;

			m_TcpClient.ReceiveTimeout = timeout;

			NetworkStream Stream = m_TcpClient.GetStream( );
			
			int BytesRead = Stream.Read( m_ReadBytes, 0, m_ReadBytes.Length );

			if( BytesRead > 0 )
			{
				MessageReceived = m_Message.AddBytes( m_ReadBytes, BytesRead );
			}
			else
			{
				// Server disconnected us.

				m_Connected = false;
			}

			return MessageReceived;
		}


	}
}
